/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER)

#include "umc_avs_dec_processing_unit_cc.h"
#include "umc_avs_pic.h"
#include "umc_base_codec.h"
#include "umc_mutex.h"
#include "umc_automatic_mutex.h"

namespace UMC
{

AVSColorProcessingUnit::AVSColorProcessingUnit(void)
{
    m_nextPTS = 0.0;

} // AVSColorProcessingUnit::AVSColorProcessingUnit(void)

AVSColorProcessingUnit::~AVSColorProcessingUnit(void)
{

} // AVSColorProcessingUnit::~AVSColorProcessingUnit(void)

Status AVSColorProcessingUnit::Init(Mutex *pGuard)
{
    // touch unreferenced parameter
    pGuard = pGuard;

    // it is not a correct function to initialize
    return UMC_ERR_FAILED;

} // Status AVSColorProcessingUnit::Init(Mutex *pGuard)

Status AVSColorProcessingUnit::Init(Mutex *pGuard, BaseCodec *pConverter)
{
    Status umcRes;

    // call the parent's method
    umcRes = AVSProcessingUnit::Init(pGuard);
    if (UMC_OK != umcRes)
        return umcRes;

    m_pConverter = pConverter;

    // reset next assumed PTS
    m_nextPTS = 0.0;

    return UMC_OK;

} // Status AVSColorProcessingUnit::Init(Mutex *pGuard, BaseCodec *pConverter)

bool AVSColorProcessingUnit::LoadJob(AVSListElement<AVSFrame> *pFrameList)
{
    AVSListElement<AVSFrame> *pToShow, *(pPrevRefs[2]);

    pToShow = NULL;
    pPrevRefs[0] = NULL;
    pPrevRefs[1] = NULL;

    // run through the list and find the unshown item
    while ((pFrameList) &&
           (pFrameList->IsReady()))
    {
        if (false == pFrameList->IsShown())
        {
            if (pFrameList->IsReference())
            {
                // first reference frame should be shown first
                if (NULL == pPrevRefs[0])
                {
                    pToShow = pFrameList;
                    break;
                }
                else if (false == pPrevRefs[0]->IsShown())
                {
                    pToShow = pPrevRefs[0];
                    break;
                }
            }
            else
            {
                // any non-reference frame can be shown
                pToShow = pFrameList;
                break;
            }
        }

        // save the previous reference frame
        if (pFrameList->IsReference())
        {
             pPrevRefs[1] = pPrevRefs[0];
             pPrevRefs[0] = pFrameList;
        }
        else
        {
             pPrevRefs[1] = 0;
        }

        // get the next frame
        pFrameList = pFrameList->GetNext();
    }

    // when there is a sequence of 3 references,
    // we can show first 2 references,
    // dispite the completeness of the 3rd frame.
    // Of course we need make sure, that there is no
    // other frames between
    if ((pFrameList) &&
        (pFrameList->IsReference()) &&
        (pPrevRefs[1]) &&
        (pFrameList->GetNext()) &&
        (false == pPrevRefs[0]->IsShown()))
    {
        pToShow = pPrevRefs[0];
    }

    // show the last reference
    if (NULL == pFrameList)
    {
        pToShow = pPrevRefs[0];
    }

    if (pToShow)
    {
        // save parameters
        m_pFrame = pToShow;

        return true;
    }

    return false;

} // bool AVSColorProcessingUnit::LoadJob(AVSListElement<AVSFrame> *pFrameList)

Status AVSColorProcessingUnit::DoJob(void)
{
    AVS_COLOR_CONVERTING_CONTEXT &ctx = m_pFrame->m_cnvCtx;
    Status umcRes;

    if (m_pConverter)
    {
        // prepare source data
        umcRes = ctx.m_picSrc.Init(m_pFrame->m_sizeLuma.width,
                                   m_pFrame->m_sizeLuma.height,
                                   m_pFrame->m_colorFormat);
        if (UMC_OK == umcRes)
        {
            size_t nFrameSize = m_pFrame->GetBufferSize();
            ctx.m_picSrc.SetBufferPointer(m_pFrame->m_pPlanes8u[0],
                                          nFrameSize);
            ctx.m_picSrc.SetPlanePointer(m_pFrame->m_pPlanes8u[0], 0);
            ctx.m_picSrc.SetPlanePitch(m_pFrame->m_iPitch, 0);
            ctx.m_picSrc.SetPlanePointer(m_pFrame->m_pPlanes8u[1], 1);
            ctx.m_picSrc.SetPlanePitch(m_pFrame->m_iPitch, 1);
            ctx.m_picSrc.SetPlanePointer(m_pFrame->m_pPlanes8u[2], 2);
            ctx.m_picSrc.SetPlanePitch(m_pFrame->m_iPitch, 2);
            ctx.m_picSrc.SetDataSize(nFrameSize);

            // do color conversion
            m_pConverter->GetFrame(&ctx.m_picSrc, ctx.m_pPicDst);

            // set frame time
            {
                Ipp64f dPicTime = m_pFrame->GetTime();

                // we need to calculate new PTS
                if (0.0 > dPicTime)
                {
                    dPicTime = m_nextPTS;
                    // recalculate the next PTS
                    m_nextPTS += 1.0 / m_pFrame->m_seqHeader.frame_rate;
                }

                // set PTS of the destination picture
                ctx.m_pPicDst->SetTime(dPicTime);
            }
        }
    }

    // adjust parameters
    UnloadJob();

    return UMC_OK;

} // Status AVSColorProcessingUnit::DoJob(void)

Status AVSColorProcessingUnit::HandleError(void)
{
    // adjust parameters
    UnloadJob();

    return UMC_OK;

} // Status AVSColorProcessingUnit::HandleError(void)

void AVSColorProcessingUnit::UnloadJob(void)
{
    AutomaticMutex guard(m_pGuard->ExtractHandle());

    m_pFrame->SetShown();

} // void AVSColorProcessingUnit::UnloadJob(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER)
